/*
 * jQuery MultiSelect UI ImportExport Filtering Plugin 1.5pre
 * Copyright (c) 2012 Eric Hynds
 *
 * http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
 *
 * Depends:
 *   - jQuery UI MultiSelect widget
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 */
define(['jquery'], function ($) {
  var rEscape = /[\-\[\]{}()*+?.,\\\^$|#\s]/g;

  $.widget('ech.multiselectfilter', {
    options: {
      label: 'Filter:',
      width: null /* override default width set in css file (px). null will inherit */,
      placeholder: 'Enter keywords',
      autoReset: false,
    },

    _create: function () {
      var opts = this.options;
      var elem = $(this.element);

      // get the multiselect instance
      var instance = (this.instance = elem.data('echMultiselect') || elem.data('multiselect'));

      // store header; add filter class so the close/check all/uncheck all links can be positioned correctly
      var header = (this.header = instance.menu.find('.ui-multiselect-header').addClass('ui-multiselect-hasfilter'));

      // wrapper elem
      var wrapper = (this.wrapper = $(
        '<div class="ui-multiselect-filter">' +
          (opts.label.length ? opts.label : '') +
          '<input placeholder="' +
          opts.placeholder +
          '" type="search"' +
          (/\d/.test(opts.width) ? 'style="width:' + opts.width + 'px"' : '') +
          ' /></div>'
      ).prependTo(this.header));

      // reference to the actual inputs
      this.inputs = instance.menu.find('input[type="checkbox"], input[type="radio"]');

      // build the input box
      this.input = wrapper.find('input').bind({
        keydown: function (e) {
          // prevent the enter key from submitting the form / closing the widget
          if (e.which === 13) {
            e.preventDefault();
          }
        },
        keyup: $.proxy(this._handler, this),
        click: $.proxy(this._handler, this),
      });

      // cache input values for searching
      this.updateCache();

      // rewrite internal _toggleChecked fn so that when checkAll/uncheckAll is fired,
      // only the currently filtered elements are checked
      instance._toggleChecked = function (flag, group) {
        var $inputs = group && group.length ? group : this.labels.find('input');
        var _self = this;

        // do not include hidden elems if the menu isn't open.
        var selector = instance._isOpen ? ':disabled, :hidden' : ':disabled';

        $inputs = $inputs.not(selector).each(this._toggleState('checked', flag));

        // update text
        this.update();

        // gather an array of the values that actually changed
        var values = $inputs
          .map(function () {
            return this.value;
          })
          .get();

        // select option tags
        this.element.find('option').filter(function () {
          if (!this.disabled && $.inArray(this.value, values) > -1) {
            _self._toggleState('selected', flag).call(this);
          }
        });

        // trigger the change event on the select
        if ($inputs.length) {
          this.element.trigger('change');
        }
      };

      // rebuild cache when multiselect is updated
      var doc = $(document).on(
        'multiselectrefresh',
        $.proxy(function () {
          this.updateCache();
          this._handler();
        }, this)
      );

      // automatically reset the widget on close?
      if (this.options.autoReset) {
        doc.on('multiselectclose', $.proxy(this._reset, this));
      }
    },

    // thx for the logic here ben alman
    _handler: function (e) {
      var term = $.trim(this.input[0].value.toLowerCase()),
        // speed up lookups
        rows = this.rows,
        inputs = this.inputs,
        cache = this.cache;

      if (!term) {
        rows.show();
      } else {
        rows.hide();

        var regex = new RegExp(term.replace(rEscape, '\\$&'), 'gi');

        this._trigger(
          'filter',
          e,
          $.map(cache, function (v, i) {
            var found = false;
            if (v.search(regex) !== -1) {
              found = true;
            } else {
              // look for 'value' attibute if innerHTML doesn't match
              var val = rows.eq(i).find('input').val();
              if (val.search(regex) !== -1) {
                found = true;
              }
            }

            if (found) {
              rows.eq(i).show();
              return inputs.get(i);
            }

            return null;
          })
        );
      }

      // show/hide optgroups
      this.instance.menu.find('.ui-multiselect-optgroup-label').each(function () {
        var $this = $(this);
        var isVisible = $this.nextUntil('.ui-multiselect-optgroup-label').filter(function () {
          return $.css(this, 'display') !== 'none';
        }).length;

        $this[isVisible ? 'show' : 'hide']();
      });
    },

    _reset: function () {
      this.input.val('').trigger('keyup');
    },

    updateCache: function () {
      // each list item
      this.rows = this.instance.menu.find('.ui-multiselect-checkboxes li:not(.ui-multiselect-optgroup-label)');

      // cache
      this.cache = this.element
        .children()
        .map(function () {
          var elem = $(this);

          // account for optgroups
          if (this.tagName.toLowerCase() === 'optgroup') {
            elem = elem.children();
          }

          return elem
            .map(function () {
              return this.innerHTML.toLowerCase();
            })
            .get();
        })
        .get();
    },

    widget: function () {
      return this.wrapper;
    },

    destroy: function () {
      $.Widget.prototype.destroy.call(this);
      this.input.val('').trigger('keyup');
      this.wrapper.remove();
    },
  });
});
